package lru

import (
	"container/list"
	"fmt"
	"reflect"
	"unsafe"
)

type Cache struct {
	maxBytes int64 //最大内存
	nbytes   int64 //目前已使用内存
	ll       *list.List
	cache    map[string]*list.Element
}

type MB int
type listData struct {
	key  string
	data []byte
}

const (
	_MB = 1024 * 1024
)

func New(max MB) *Cache {
	return &Cache{maxBytes: int64(max * _MB), nbytes: 0, ll: list.New(), cache: make(map[string]*list.Element)}
}

func (c Cache) GetMemory() int64 {
	return c.nbytes
}
func getByteSize(bs []byte) int64 {
	fmt.Println("len:", len(bs))
	return int64(int(unsafe.Sizeof(bs)) + len(bs)*int(unsafe.Sizeof(bs[0])))
}
func (c *Cache) AddOrUpdate(k string, v []byte) {
	if kv := c.cache[k]; kv != nil {
		//update
		kv.Value = listData{
			key:  k,
			data: v,
		}
		c.ll.MoveToFront(kv)
		c.nbytes += getByteSize(kv.Value.(listData).data) - getByteSize(v)
	} else {
		//add
		c.ll.PushFront(listData{
			key:  k,
			data: v,
		})
		c.cache[k] = c.ll.Front()
		c.nbytes += getByteSize(v)
	}
	if c.nbytes >= c.maxBytes {
		//缓存淘汰
		e := c.ll.Back()
		delete(c.cache, e.Value.(listData).key)
		c.ll.Remove(e)
		c.nbytes -= int64(int(reflect.TypeOf(e.Value.(listData).data).Size()))
	}

}
func (c Cache) Keys() []string {
	var keys []string
	for k, _ := range c.cache {
		keys = append(keys, k)
	}
	return keys
}
func (c *Cache) Remove(key string) {
	kv := c.cache[key]
	c.ll.Remove(kv)
	c.nbytes = int64(int(c.nbytes) - int(reflect.TypeOf(kv.Value.(listData).data).Size()))
}

func (c *Cache) Select(k string) []byte {
	v := c.cache[k]
	if v == nil {
		return nil
	}
	c.ll.MoveToFront(v)
	return v.Value.(listData).data
}

func (c Cache) HasKey(k string) bool {
	return c.cache[k] != nil
}
